CurveTexture.ts 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259
  1. /*
  2. CocosCreater 3.8.x 2d曲线纹理插件
  3. 可用来制作2d横版的地形,如:山脉,道路等,类似登山赛车和滑雪大冒险的横版平滑的地图
  4. CocosCreater 3.8.x 2d curve texture plugin
  5. Can be used to make 2d side-scrolling terrain, such as: mountains, roads, etc., similar to the smooth maps in Hill Climb Racing and Ski Safari
  6. 作者:soida
  7. Author: soida
  8. 微信:soida3
  9. WeChat: soida3
  10. Cocos商店(https://store.cocos.com)也有出售,可直接搜索soida或者关键词'曲线纹理'
  11. Also available in the Cocos store(https://store.cocos.com), just search the keyword 'soida' '曲线纹理' 'curve texture'
  12. */
  13. // import { EDITOR, EDITOR_NOT_IN_PREVIEW } from 'cc/env';
  14. import {
  15. _decorator,
  16. Asset,
  17. AssetManager,
  18. CCBoolean,
  19. CCFloat,
  20. CCInteger,
  21. Color,
  22. Component,
  23. EffectAsset,
  24. Enum,
  25. ERigidBody2DType,
  26. EventKeyboard,
  27. gfx,
  28. Input,
  29. input,
  30. KeyCode,
  31. Mat4,
  32. Material,
  33. Mesh,
  34. MeshRenderer,
  35. misc,
  36. Node,
  37. PolygonCollider2D,
  38. RenderTexture,
  39. resources,
  40. RigidBody2D,
  41. Sprite,
  42. SpriteFrame,
  43. sys,
  44. Texture2D,
  45. TransformBit,
  46. tween,
  47. Tween,
  48. UIMeshRenderer,
  49. UIOpacity,
  50. UITransform,
  51. utils,
  52. v2,
  53. v3,
  54. v4,
  55. Vec2,
  56. Vec3,
  57. } from 'cc';
  58. import { EDITOR, EDITOR_NOT_IN_PREVIEW } from 'cc/env';
  59. import { MoveWithTouch } from './MoveWithTouch';
  60. import { UsefulTools } from './UsefulTools';
  61. import { MeshSprite } from './MeshSprite';
  62. const { ccclass, property, executeInEditMode, requireComponent, disallowMultiple, menu, help } = _decorator;
  63. function getGroupStyleName() {
  64. if (EDITOR) {
  65. //@ts-ignore
  66. const v = Editor.App.version;
  67. return UsefulTools.compareVersions(v, '3.8.0') >= 0 ? 'section' : 'tab';
  68. }
  69. return 'tab';
  70. }
  71. const GROUP_STYLE_NAME: string = getGroupStyleName();
  72. //兼容低版本未定义
  73. if (EDITOR_NOT_IN_PREVIEW === undefined) {
  74. console.warn('当前环境无 EDITOR_NOT_IN_PREVIEW 定义,开始兼容性定义');
  75. const EDITOR_NOT_IN_PREVIEW = EDITOR;
  76. }
  77. //判断Vec2类的原型是否有toVec3()方法,如果没有就添加一个
  78. //@ts-ignore
  79. if (!Vec2.prototype.toVec3) {
  80. console.warn('当前环境无 Vec2.toVec3() 定义,开始兼容性定义');
  81. //@ts-ignore
  82. Vec2.prototype.toVec3 = function (this: Vec2, z: number = 0): Vec3 {
  83. return new Vec3(this.x, this.y, z);
  84. };
  85. }
  86. //判断Vec3类的原型是否有toVec2()方法,如果没有就添加一个
  87. //@ts-ignore
  88. if (!Vec3.prototype.toVec2) {
  89. console.warn('当前环境无 Vec3.toVec2() 定义,开始兼容性定义');
  90. //@ts-ignore
  91. Vec3.prototype.toVec2 = function (this: Vec3): Vec2 {
  92. return new Vec2(this.x, this.y);
  93. };
  94. }
  95. /**
  96. *
  97. * @returns 是否编辑器中文环境,非运行环境 'Whether the editor is in Chinese environment, not running environment'
  98. */
  99. export function inEditorZH(): boolean {
  100. if (EDITOR) {
  101. //@ts-ignore
  102. return Editor.I18n.getLanguage() === 'zh';
  103. }
  104. return false;
  105. }
  106. const IN_EDITOR_ZH: boolean = inEditorZH();
  107. function isCloseToVec3(self: Vec3, other: Vec3, epsilon = 0.00001): boolean {
  108. const dx = self.x - other.x;
  109. const dy = self.y - other.y;
  110. const dz = self.z - other.z;
  111. const squaredDistance = dx * dx + dy * dy + dz * dz;
  112. return squaredDistance <= epsilon * epsilon;
  113. }
  114. function isCloseToVec2(self: Vec2, other: Vec2, epsilon = 0.00001): boolean {
  115. const dx = self.x - other.x;
  116. const dy = self.y - other.y;
  117. const squaredDistance = dx * dx + dy * dy;
  118. return squaredDistance <= epsilon * epsilon;
  119. }
  120. export enum TerrainRenderMode {
  121. VERTICAL_MODE = 0, //正常竖直模式,按照控制点竖直向下渲染
  122. TANGENT_MODE = 1, //切线模式,按照控制点的切线垂直方向向下渲染
  123. TANGENT_MODE_CENTER = 2, //切线模式,按照控制点在中心点,向上下各渲染一半的纹理高度
  124. }
  125. export enum TerrainRenderUVMode {
  126. UV_REPEAT_GRID = 0, //uv计算按照默认网格重复
  127. UV_REPEAT_LENGTH = 1, //uv计算按照长度重复
  128. }
  129. @ccclass('CurveTexture')
  130. @requireComponent(MeshRenderer)
  131. @disallowMultiple
  132. @executeInEditMode
  133. @help('https://github.com/soidaken/CurveTexture2d')
  134. @menu('2D/CurveTexture2d(曲线纹理2d)')
  135. export class CurveTexture extends Component {
  136. private _curve_width: number = 512;
  137. private _curve_height: number = 512;
  138. private _curve_height_result: number = 512;
  139. private _curve_collider_thickness: number = 0;
  140. // private _isZH: boolean = sys.language == 'zh';
  141. private _headNode: Node | null = null;
  142. private _tailNode: Node | null = null;
  143. @property({})
  144. private _syncOnOff: boolean = false;
  145. @property({
  146. tooltip: IN_EDITOR_ZH
  147. ? '自动同步另一个节点的地形数据,开启后此节点无法编辑地形'
  148. : ' (Automatically sync terrain data from another node, this node cannot edit terrain when enabled)',
  149. displayName: IN_EDITOR_ZH ? '开启跟随模式' : ' Enable Follow Mode',
  150. })
  151. get syncOnOff() {
  152. return this._syncOnOff;
  153. }
  154. set syncOnOff(value) {
  155. if (!value) {
  156. this.syncTarget = null;
  157. }
  158. this._syncOnOff = value;
  159. this._refreshAll();
  160. }
  161. @property({ type: Node })
  162. private _syncTarget: Node | null = null;
  163. @property({
  164. type: Node,
  165. tooltip: IN_EDITOR_ZH ? '跟随的目标节点' : ' (Target node to follow)',
  166. displayName: IN_EDITOR_ZH ? '跟随目标' : ' (Follow Target)',
  167. // visible() {
  168. // return this._syncOnOff;
  169. // },
  170. })
  171. get syncTarget() {
  172. return this._syncTarget;
  173. }
  174. set syncTarget(value) {
  175. if (this._syncOnOff) {
  176. if (value) {
  177. let c = value.getComponent(CurveTexture);
  178. if (!c) {
  179. console.warn(
  180. IN_EDITOR_ZH
  181. ? '跟随目标节点没有CurveTexture组件,请添加'
  182. : ' (The target node to follow does not have a CurveTexture component, please add it)'
  183. );
  184. return;
  185. }
  186. if (value === this.node) {
  187. console.warn(IN_EDITOR_ZH ? '跟随目标节点不能是自己' : ' (The target node to follow cannot be itself)');
  188. return;
  189. }
  190. //防止循环跟随 'Prevent circular following'
  191. if (c.syncTarget) {
  192. if (c.syncTarget === this.node) {
  193. console.warn(
  194. IN_EDITOR_ZH ? '出现循环跟随啦,非法操作!' : ' (Circular following detected, illegal operation!)'
  195. );
  196. return;
  197. }
  198. }
  199. }
  200. this._syncTarget = value;
  201. this._refreshAll();
  202. } else {
  203. console.warn(IN_EDITOR_ZH ? '需要先开启跟随模式' : ' (Follow mode needs to be enabled first)');
  204. }
  205. }
  206. @property({ type: Enum(TerrainRenderMode) })
  207. private _renderMode: TerrainRenderMode = TerrainRenderMode.VERTICAL_MODE;
  208. @property({
  209. type: Enum(TerrainRenderMode),
  210. tooltip: IN_EDITOR_ZH
  211. ? '渲染顶点模式,VERTICAL_MODE为按照控制点竖直向下渲染,适合厚纹理;TANGENT_MODE/TANGENT_MODE_CENTER为按照控制点的切线垂直方向向下渲染,适合细纹理'
  212. : ' (Terrain rendering mode, VERTICAL_MODE renders vertically downward according to control points, suitable for thick textures; TANGENT_MODE/TANGENT_MODE_CENTER renders vertically downward according to the tangent direction of control points, suitable for thin textures)',
  213. displayName: IN_EDITOR_ZH ? '渲染顶点模式' : ' Terrain Rendering Mod',
  214. group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  215. })
  216. get renderMode() {
  217. return this._renderMode;
  218. }
  219. set renderMode(value) {
  220. this._renderMode = value;
  221. // if (value == TerrainRenderMode.TANGENT_MODE || value == TerrainRenderMode.TANGENT_MODE_CENTER) {
  222. // this._thickness = 0;
  223. // }
  224. this._refreshAll();
  225. }
  226. @property({ type: Enum(TerrainRenderUVMode) })
  227. private _renderUVMode: TerrainRenderUVMode = TerrainRenderUVMode.UV_REPEAT_GRID;
  228. @property({
  229. type: Enum(TerrainRenderUVMode),
  230. tooltip: IN_EDITOR_ZH
  231. ? '渲染UV模式,UV_REPEAT_GRID为按照默认网格重复;UV_REPEAT_LENGTH为按照长度重复'
  232. : ' (Terrain UV rendering mode, UV_REPEAT_GRID repeats according to the default grid; UV_REPEAT_LENGTH repeats according to length)',
  233. displayName: IN_EDITOR_ZH ? '渲染UV模式' : ' Terrain Rendering UV Mod',
  234. group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  235. })
  236. get renderUVMode() {
  237. return this._renderUVMode;
  238. }
  239. set renderUVMode(value) {
  240. this._renderUVMode = value;
  241. // if (value == TerrainRenderMode.TANGENT_MODE || value == TerrainRenderMode.TANGENT_MODE_CENTER) {
  242. // this._thickness = 0;
  243. // }
  244. this._refreshAll();
  245. }
  246. @property({ type: EffectAsset })
  247. private _spEffectAsset: EffectAsset | null = null;
  248. @property({
  249. type: EffectAsset,
  250. tooltip: IN_EDITOR_ZH ? '渲染effect文件' : 'effect asset',
  251. displayName: IN_EDITOR_ZH ? '渲染effect文件' : 'effect asset',
  252. group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  253. })
  254. get spEffectAsset() {
  255. return this._spEffectAsset;
  256. }
  257. set spEffectAsset(value) {
  258. this._spEffectAsset = value;
  259. this._refreshAll();
  260. }
  261. private _gizmoInitColor: Color = new Color(255, 255, 255, 255);
  262. @property({ type: SpriteFrame })
  263. private _spFrame: SpriteFrame | null = null;
  264. @property({
  265. type: SpriteFrame,
  266. tooltip: IN_EDITOR_ZH ? '渲染纹理' : ' Terrain texture',
  267. displayName: IN_EDITOR_ZH ? '渲染纹理' : ' Terrain Texture',
  268. group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  269. })
  270. get spFrame() {
  271. return this._spFrame;
  272. }
  273. set spFrame(value) {
  274. this._spFrame = value;
  275. //取纹理一片区域的颜色均值作为gizmo的颜色
  276. const uint8arr = RenderTexture.prototype.readPixels.call(value.texture, 0, this._spFrame.height / 2 - 2, 4, 4);
  277. let r = 0,
  278. g = 0,
  279. b = 0;
  280. for (let i = 0; i < uint8arr.length; i += 4) {
  281. r += uint8arr[i];
  282. g += uint8arr[i + 1];
  283. b += uint8arr[i + 2];
  284. //忽略alpha通道
  285. }
  286. r /= 16;
  287. g /= 16;
  288. b /= 16;
  289. // 将rgb值调整为同色系并提升亮度两个等级
  290. let [h, s, l] = UsefulTools.rgbToHsl(r, g, b);
  291. // 提升亮度两个等级(每级约0.08,提升0.16,最大不超过0.95)
  292. l = Math.min(l + 0.16, 0.95);
  293. [r, g, b] = UsefulTools.hslToRgb(h, s, l);
  294. this._gizmoInitColor.set(r, g, b, this.gizmoColor.a);
  295. // console.log('this._gizmoInitColor ', this._gizmoInitColor);
  296. this.gizmoColor = this._gizmoInitColor;
  297. this._refreshAll();
  298. }
  299. @property({})
  300. private _headTailFixedOnOff: boolean = false;
  301. @property({
  302. tooltip: IN_EDITOR_ZH ? '首尾是否有固定纹理' : 'head/tail fixed texture',
  303. displayName: IN_EDITOR_ZH ? '首尾是否有固定纹理' : 'head/tail fixed texture',
  304. group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  305. visible(this: CurveTexture) {
  306. return (
  307. this._renderMode == TerrainRenderMode.TANGENT_MODE || this._renderMode == TerrainRenderMode.TANGENT_MODE_CENTER
  308. );
  309. },
  310. })
  311. get headTailFixedOnOff() {
  312. return this._headTailFixedOnOff;
  313. }
  314. set headTailFixedOnOff(value) {
  315. this._headTailFixedOnOff = value;
  316. this._refreshAll();
  317. }
  318. @property({ type: SpriteFrame })
  319. private _headTailFrame: SpriteFrame | null = null;
  320. @property({
  321. type: SpriteFrame,
  322. tooltip: IN_EDITOR_ZH ? '首尾纹理' : ' Terrain texture',
  323. displayName: IN_EDITOR_ZH ? '首尾纹理' : ' Terrain Texture',
  324. group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  325. visible(this: CurveTexture) {
  326. return this._headTailFixedOnOff == true;
  327. },
  328. })
  329. get headTailFrame() {
  330. return this._headTailFrame;
  331. }
  332. set headTailFrame(value) {
  333. this._headTailFrame = value;
  334. this._refreshAll();
  335. }
  336. // @property({})
  337. // private _alphaThreshold: number = 0.4;
  338. // @property({
  339. // tooltip: IN_EDITOR_ZH
  340. // ? '如果纹理有透明渲染异常,可以调整这里'
  341. // : ' (If there is a transparent rendering issue with the texture, you can adjust here)',
  342. // displayName: IN_EDITOR_ZH ? '如果纹理有透明' : ' If the texture has transparency',
  343. // group: IN_EDITOR_ZH ? '渲染属性' : '(Terrain Rendering)',
  344. // slide: true,
  345. // range: [0, 1, 0.01],
  346. // })
  347. // get alphaThreshold() {
  348. // return this._alphaThreshold;
  349. // }
  350. // set alphaThreshold(value) {
  351. // this._alphaThreshold = value;
  352. // this._refreshAll();
  353. // }
  354. @property({})
  355. private _updownFix: boolean = false;
  356. @property({
  357. tooltip: IN_EDITOR_ZH ? '如果上下反了,就来点点我!' : ' (If it is upside down, click me!)',
  358. displayName: IN_EDITOR_ZH ? '上下反了?' : ' (Upside Down?)',
  359. group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
  360. })
  361. get updownFix() {
  362. return this._updownFix;
  363. }
  364. set updownFix(value) {
  365. this._updownFix = value;
  366. this._refreshAll();
  367. }
  368. @property({})
  369. private _thickness: number = 0;
  370. @property({
  371. tooltip: IN_EDITOR_ZH
  372. ? '地形的厚度,填0是设置为纹理高度值'
  373. : ' (Thickness of the terrain, 0 sets it to the texture height value)',
  374. displayName: IN_EDITOR_ZH ? '渲染厚度' : ' (Terrain Thickness)',
  375. group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
  376. // visible(this: CurveTexture) {
  377. // return this._renderMode == TerrainRenderMode.VERTICAL_MODE;
  378. // },
  379. })
  380. get thickness() {
  381. return this._thickness;
  382. }
  383. set thickness(value) {
  384. this._thickness = value;
  385. this._refreshAll();
  386. }
  387. @property({})
  388. private _offset: Vec3 = v3(0, 0, 0);
  389. @property({
  390. type: Vec3,
  391. tooltip: IN_EDITOR_ZH
  392. ? '渲染起始的偏移值,z暂时无效'
  393. : ' (Offset value for rendering start, z is temporarily invalid)',
  394. group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
  395. displayName: IN_EDITOR_ZH ? '渲染偏移' : ' (Rendering Offset)',
  396. })
  397. get offset() {
  398. return this._offset;
  399. }
  400. set offset(value) {
  401. this._offset = value;
  402. this._offset.z = 0;
  403. this._refreshAll();
  404. }
  405. // @property({})
  406. private _controlPoints: Vec3[] = [v3(-500, -130, 0), v3(-160, 30, 0), v3(160, -130, 0), v3(500, 30, 0)];
  407. get controlPoints() {
  408. return this._controlPoints;
  409. }
  410. set controlPoints(value) {
  411. this._controlPoints = null;
  412. this._controlPoints = value.slice();
  413. this._refreshAll();
  414. }
  415. @property({})
  416. private _smoothness: number = 16;
  417. @property({
  418. type: CCInteger,
  419. tooltip: IN_EDITOR_ZH
  420. ? '每两控制点之间细分的数量,默认16,如果是0,则不细分'
  421. : ' (Number of subdivisions between every two control points, default is 16)',
  422. displayName: IN_EDITOR_ZH ? '细分点数量' : ' (Number of Subdivided Points)',
  423. group: IN_EDITOR_ZH ? '渲染属性' : ' (Terrain Rendering)',
  424. slide: true,
  425. range: [0, 32, 2],
  426. })
  427. get smoothness() {
  428. return this._smoothness;
  429. }
  430. set smoothness(value) {
  431. this._smoothness = value % 2 == 1 ? value + 1 : value;
  432. this._refreshAll();
  433. }
  434. @property({})
  435. private _headTailCloseOnoff: boolean = false;
  436. @property({
  437. tooltip: IN_EDITOR_ZH
  438. ? '是否首尾闭合,渲染为一个闭合的区域'
  439. : ' (Whether the head and tail are closed, rendered as a closed area)',
  440. displayName: IN_EDITOR_ZH ? '是否首尾闭合' : ' (Head and Tail Closure)',
  441. group: IN_EDITOR_ZH ? '首尾闭合属性' : ' (Head and Tail Closure Parameters)',
  442. })
  443. get headTailCloseOnoff() {
  444. return this._headTailCloseOnoff;
  445. }
  446. set headTailCloseOnoff(value) {
  447. if (!value) {
  448. this._headTailCloseOnoff = false;
  449. this._refreshAll();
  450. return;
  451. }
  452. //检测闭合后的顶点组成的多边形是否符合规则,1:不能自交 2:不能有连续共线点 3:不能有重复点
  453. //先检测控制点,符合规则后看有无必要再检测细分后的点
  454. const result2 = UsefulTools.isPolygonSelfIntersect(this._controlPoints);
  455. if (result2.result) {
  456. // console.log('length ', this._editNodes.length);
  457. if (this._editNodes) {
  458. const n1 = this._editNodes[result2.index];
  459. const n2 = this._editNodes[result2.index + 1];
  460. n1.setScale(1.2, 1.2, 1);
  461. setTimeout(() => {
  462. n1.setScale(1, 1, 1);
  463. }, 250);
  464. n2.setScale(1.2, 1.2, 1);
  465. setTimeout(() => {
  466. n2.setScale(1, 1, 1);
  467. }, 250);
  468. }
  469. console.warn(
  470. IN_EDITOR_ZH
  471. ? `${this.node.name} 控制点有自相交,请检查! `
  472. : ' (Control points have self-intersection, please check!)'
  473. );
  474. return;
  475. }
  476. const result1 = UsefulTools.isCollinear(this._controlPoints);
  477. if (result1.result) {
  478. console.warn(
  479. IN_EDITOR_ZH
  480. ? `${this.node.name} 控制点有连续共线点,请检查! 点索引:${result1.index}/${result1.index + 1}/${
  481. result1.index + 2
  482. }`
  483. : ' (Control points have continuous collinear points, please check!)'
  484. );
  485. return;
  486. }
  487. if (UsefulTools.hasDuplicatePoints(this._controlPoints)) {
  488. console.warn(
  489. IN_EDITOR_ZH
  490. ? `${this.node.name} 控制点有重复点,请检查!`
  491. : ' (Control points have duplicate points, please check!)'
  492. );
  493. return;
  494. }
  495. this._headTailCloseOnoff = value;
  496. this._refreshAll();
  497. }
  498. @property({ type: MeshSprite })
  499. private _innerMeshSprite: MeshSprite | null = null;
  500. @property({
  501. tooltip: IN_EDITOR_ZH
  502. ? '闭合后内部渲染组件,如果不需要可以不设置'
  503. : ' (Inner rendering component after closure, if not needed, it can be left unset)',
  504. displayName: IN_EDITOR_ZH ? '闭合后内部渲染组件' : ' (Head and Tail Closure)',
  505. group: IN_EDITOR_ZH ? '首尾闭合属性' : ' (Head and Tail Closure Parameters)',
  506. type: MeshSprite,
  507. visible(this: CurveTexture) {
  508. return this._headTailCloseOnoff;
  509. },
  510. })
  511. get innerMeshSprite(): MeshSprite {
  512. return this._innerMeshSprite;
  513. }
  514. set innerMeshSprite(value: MeshSprite) {
  515. this._innerMeshSprite = value;
  516. this._refreshAll();
  517. }
  518. @property({})
  519. private _physcisOnOff: boolean = false;
  520. @property({
  521. tooltip: IN_EDITOR_ZH
  522. ? '在一个特定子节点上添加[PhysicsPolygonCollider]组件,你也可以通过[getCurveTexturePoints] 获取数据后自己实现.'
  523. : 'Add a [PhysicsPolygonCollider] component to a specific child node, or you can implement it yourself after fetching data from [getCurveTexturePoints].)',
  524. displayName: IN_EDITOR_ZH ? '同步生成物理属性' : ' (Synchronize Generation of Physical Properties)',
  525. group: IN_EDITOR_ZH ? '物理属性' : ' (Physical Properties)',
  526. })
  527. get physcisOnOff() {
  528. return this._physcisOnOff;
  529. }
  530. set physcisOnOff(value) {
  531. this._physcisOnOff = value;
  532. this._refreshAll();
  533. }
  534. @property({})
  535. private _physicsThickness: number = 0;
  536. @property({
  537. type: CCFloat,
  538. tooltip: IN_EDITOR_ZH
  539. ? '物理碰撞器的厚度,填0是设置为纹理高度值'
  540. : ' (Thickness of the physical collider, 0 sets it to the texture height value)',
  541. displayName: IN_EDITOR_ZH ? '物理碰撞器厚度' : ' (Physical Collider Thickness)',
  542. group: IN_EDITOR_ZH ? '物理属性' : ' (Physical Properties)',
  543. visible(this: CurveTexture) {
  544. return this._physcisOnOff;
  545. },
  546. })
  547. get physicsThickness() {
  548. return this._physicsThickness;
  549. }
  550. set physicsThickness(value) {
  551. this._physicsThickness = value;
  552. this._refreshAll();
  553. }
  554. private _physicsCollider: PolygonCollider2D | null = null;
  555. //获取对应的物理碰撞器 'Get the corresponding physical collider'
  556. public get physicsCollider() {
  557. return this._physicsCollider;
  558. }
  559. @property({})
  560. private _offsetCollider: Vec3 = v3(0, 0, 0);
  561. @property({
  562. type: Vec3,
  563. tooltip: IN_EDITOR_ZH
  564. ? '物理碰撞器的偏移值,默认(0,0,0)'
  565. : ' (Offset value for the physical collider, default is (0,0,0))',
  566. displayName: IN_EDITOR_ZH ? '物理碰撞器偏移' : ' (Physical Collider Offset)',
  567. group: IN_EDITOR_ZH ? '物理属性' : ' (Physical Properties)',
  568. visible(this: CurveTexture) {
  569. return this._physcisOnOff;
  570. },
  571. })
  572. get offsetCollider() {
  573. return this._offsetCollider;
  574. }
  575. set offsetCollider(value) {
  576. this._offsetCollider = value;
  577. this._offsetCollider.z = 0;
  578. this._refreshAll();
  579. // if (this._physicsCollider) {
  580. // this._physicsCollider.offset = this._offsetCollider.clone();
  581. // }
  582. }
  583. /**
  584. *
  585. * @returns 获取细分后的所有数据点,相对于当前节点坐标系 'Get all subdivided data points, relative to the current node coordinate system'
  586. */
  587. public getCurveTexturePoints(): Vec3[] {
  588. return this._vertexesSegment;
  589. }
  590. //
  591. // private _chainCollider: PhysicsChainCollider = null;
  592. //将编辑点 _controlPoints 细分后的平滑渲染点 'Smooth rendering points after subdividing the editing points _controlPoints'
  593. private _vertexesSegment: Vec3[] = [];
  594. /**
  595. * 获取细分后的所有数据点,相对于当前节点坐标系 'Get all subdivided data points, relative to the current node coordinate system'
  596. */
  597. get vertexesSegment() {
  598. return this._vertexesSegment;
  599. }
  600. //最终的渲染顶点数据 'Final rendering vertex data'
  601. private _vertexes: Vec3[] = [];
  602. // get vertexes() {
  603. // return this._vertexes;
  604. // }
  605. // set vertexes(value) {
  606. // this._controlPoints = value.slice();
  607. // this._refreshAll();
  608. // }
  609. private renderer: MeshRenderer | null = null;
  610. protected onLoad() {
  611. let tmp: Vec3[] = [];
  612. let snodes: Node[] = [];
  613. for (let i = 0; i < this.node.children.length; i++) {
  614. const item = this.node.children[i];
  615. if (item.name.startsWith('gizmo_')) {
  616. snodes.push(item);
  617. }
  618. }
  619. if (this._sortInXDirection) {
  620. snodes.sort((a, b) => a.x - b.x);
  621. snodes.forEach((item, index) => {
  622. item.setSiblingIndex(index + 10);
  623. });
  624. }
  625. for (let i = 0; i < snodes.length; i++) {
  626. const item = snodes[i];
  627. tmp.push(item.position);
  628. }
  629. this._controlPoints = tmp.length > 0 ? tmp : this._controlPoints;
  630. }
  631. /**
  632. * 打印调试信息 'Print debug information'
  633. * _curve_width: 纹理宽度 'Texture width'
  634. * _curve_height: 纹理高度 'Texture height'
  635. * _controlPoints: 编辑点 'Editing points'
  636. * _vertexesSegment: 细分点 'Subdivided points'
  637. * _vertexes: 渲染点 'Rendering points'
  638. */
  639. debugPrint() {
  640. console.log(this.node.name + ' CurveTexture.ts debugPrint _curve_width : ', this._curve_width);
  641. console.log(this.node.name + ' CurveTexture.ts debugPrint _curve_height : ', this._curve_height);
  642. console.log(this.node.name + ' CurveTexture.ts debugPrint _controlPoints : ', this._controlPoints);
  643. console.log(this.node.name + ' CurveTexture.ts debugPrint _vertexesSegment : ', this._vertexesSegment);
  644. console.log(this.node.name + ' CurveTexture.ts debugPrint _vertexes : ', this._vertexes);
  645. }
  646. protected onEnable() {
  647. this.node.off(Node.EventType.TRANSFORM_CHANGED);
  648. this.node.on(
  649. Node.EventType.TRANSFORM_CHANGED,
  650. (type: TransformBit) => {
  651. if (type & Node.TransformBit.TRS) {
  652. // console.log('CurveTexture.ts TRS changed, 刷新一次' + ' (refresh once)');
  653. this._refreshAll('559');
  654. }
  655. },
  656. this
  657. );
  658. this._registerKeyForGizmo();
  659. this._refreshAll('565');
  660. }
  661. private _refreshAll(from: string = '') {
  662. // console.log(`from ${this.node.name} ${from}`);
  663. let renderer = this.node.getComponent(MeshRenderer);
  664. if (!renderer) {
  665. console.warn('MeshRenderer component not found!');
  666. return;
  667. }
  668. this.renderer = renderer;
  669. //强制转换2d渲染模式,使用2d的层级管理
  670. let uiMeshR = this.node.getComponent(UIMeshRenderer);
  671. if (!uiMeshR) {
  672. this.node.addComponent(UIMeshRenderer);
  673. }
  674. if (!this.renderer) {
  675. console.warn(
  676. IN_EDITOR_ZH
  677. ? '没有MeshRenderer组件,请添加MeshRenderer组件!'
  678. : ' (No MeshRenderer component, please add MeshRenderer component!)'
  679. );
  680. return;
  681. }
  682. let csame = this.node.getComponents(CurveTexture);
  683. if (csame.length > 1) {
  684. console.error(
  685. this.node.name + IN_EDITOR_ZH
  686. ? ' 请去除无用的[ CurveTexture ]组件,一个节点只需绑定一个,多个存在只会有最后一个生效!'
  687. : ' (Please remove unnecessary [CurveTexture] components, only one needs to be bound to a node, if multiple exist, only the last one will take effect!)'
  688. );
  689. }
  690. if (!this._spFrame) {
  691. console.warn(
  692. IN_EDITOR_ZH
  693. ? '还未指定 [ 渲染纹理 ],拖入插件默认的 [ terrain-ground ] 贴图即可!'
  694. : ' (The [Terrain Texture] has not been specified yet, drag in the default [terrain-ground] texture of the plugin!)'
  695. );
  696. return;
  697. }
  698. if (!this._spEffectAsset) {
  699. console.warn(
  700. IN_EDITOR_ZH
  701. ? '还未指定 [ 渲染effect文件 ],拖入插件默认的 [ curvetexure-sprite.effect ] effect文件即可!'
  702. : ' (The [Effect Asset] has not been specified yet, drag in the default [curvetexure-sprite.effect] texture of the plugin!)'
  703. );
  704. return;
  705. }
  706. this._curve_height = this._spFrame.originalSize.height; //纹理高度 'Texture height'
  707. this._curve_width = this._spFrame.originalSize.width;
  708. //检查纹理的宽和高是否是2的指数 'Check if the width and height of the texture are powers of 2'
  709. if (this._curve_width & (this._curve_width - 1)) {
  710. console.warn(
  711. IN_EDITOR_ZH
  712. ? '纹理的宽度不是2的指数,请检查!'
  713. : ' (The width of the texture is not a power of 2, please check!)'
  714. );
  715. return;
  716. }
  717. if (this._curve_height & (this._curve_height - 1)) {
  718. console.warn(
  719. IN_EDITOR_ZH
  720. ? '纹理的高度不是2的指数,请检查!'
  721. : ' (The height of the texture is not a power of 2, please check!)'
  722. );
  723. return;
  724. }
  725. if (this.gizmoTag == null) {
  726. console.warn(
  727. IN_EDITOR_ZH
  728. ? '还未指定[ 控制点纹理 ]! 拖入 [ gizmotag ] 资源即可'
  729. : ' ([ Control Point Texture] has not been specified yet! Drag in the [gizmotag] resource)'
  730. );
  731. // return;
  732. }
  733. // if (this.gizmoTagLine == null) {
  734. // console.warn(
  735. // IN_EDITOR_ZH
  736. // ? '还未指定[ 控制点连接纹理 ]! 拖入 [ gizmotagline ] 资源即可'
  737. // : ' ([ Control Line Texture] has not been specified yet! Drag in the [gizmotagline] resource)'
  738. // );
  739. // // return;
  740. // }
  741. let c = this.node.getComponent(UIOpacity);
  742. if (c) {
  743. console.warn(
  744. this.node.name +
  745. (IN_EDITOR_ZH
  746. ? ' 请去除[ UIOpacity ]组件,实际渲染忽略这个属性,如果需要透明请修改原始贴图!'
  747. : ' (Please remove the [UIOpacity] component, this property is ignored in actual rendering, if transparency is needed, please modify the original texture!)')
  748. );
  749. }
  750. this._applySpriteFrame();
  751. if (this._syncOnOff && this._syncTarget) {
  752. //同步模式下不自身生成细分点 'In sync mode, do not generate subdivided points by itself'
  753. } else {
  754. this._vertexesSegment.length = 0;
  755. this._vertexesSegment = this._generateSegmentVertex(this._smoothness);
  756. this._updateHeadTail();
  757. if (this._headTailCloseOnoff && this.innerMeshSprite) {
  758. this.innerMeshSprite.node.setWorldPosition(this.node.worldPosition);
  759. this.innerMeshSprite.setVertexes(this._vertexesSegment);
  760. }
  761. }
  762. if (this._vertexesSegment.length > 0) {
  763. this._updateMesh();
  764. this.generatePhysicData();
  765. }
  766. }
  767. private _applySpriteFrame() {
  768. if (this._spFrame && this.renderer) {
  769. this._curve_height_result = this._thickness == 0 ? this._curve_height : this._thickness;
  770. this._curve_collider_thickness = this._physicsThickness == 0 ? this._curve_height : this._physicsThickness;
  771. let texture = this._spFrame.texture;
  772. texture.setWrapMode(Texture2D.WrapMode.REPEAT, Texture2D.WrapMode.REPEAT, Texture2D.WrapMode.REPEAT);
  773. let mat = this.renderer.getMaterialInstance(0) as Material;
  774. if (!mat || mat.effectAsset !== this._spEffectAsset) {
  775. mat = new Material();
  776. mat.initialize({
  777. effectAsset: this._spEffectAsset,
  778. defines: {
  779. USE_TEXTURE: true,
  780. },
  781. });
  782. this.renderer.setMaterialInstance(mat, 0);
  783. }
  784. mat.setProperty('mainTexture', texture, 0);
  785. const uv = this._spFrame.uv;
  786. const uv_x = uv[0]; // ul
  787. const uv_y = uv[1]; // vb
  788. const uv_w = uv[6] - uv[0]; // ur - ul
  789. const uv_h = uv[5] - uv[1]; // vt - vb
  790. const altlasUV = v4(uv_x, uv_y, uv_w, uv_h);
  791. // console.log(this.node.name + ' altlasUV', altlasUV);
  792. mat.setProperty('altlasUV', altlasUV, 0);
  793. } else {
  794. if (!this._spFrame) {
  795. let mat = this.renderer!.getMaterialInstance(0) as Material;
  796. mat.setProperty('mainTexture', null, 0);
  797. }
  798. }
  799. }
  800. private _getTangentPos(current: Vec3, next: Vec3, angleRad: number, thickness: number): Vec3 {
  801. const v1 = next
  802. .clone()
  803. .subtract(current)
  804. .normalize()
  805. .toVec2()
  806. .rotate(angleRad)
  807. .multiplyScalar(thickness)
  808. .add(current.toVec2());
  809. return v1.toVec3();
  810. }
  811. private _updateHeadTail() {
  812. this._updateHead();
  813. this._updateTail();
  814. }
  815. private _transfromPositonWhenScale(inpos: Vec3, targetNode: Node) {
  816. const localPos = inpos.clone();
  817. const worldMat = targetNode.getWorldMatrix();
  818. const worldPos = new Vec3();
  819. Vec3.transformMat4(worldPos, localPos, worldMat);
  820. const invParentMat = new Mat4();
  821. Mat4.invert(invParentMat, targetNode.getWorldMatrix());
  822. const newLocalPos = new Vec3();
  823. Vec3.transformMat4(newLocalPos, worldPos, invParentMat);
  824. return newLocalPos;
  825. }
  826. private _updateHead() {
  827. if (this._headTailFixedOnOff) {
  828. if (!this._headTailFrame) {
  829. console.warn(IN_EDITOR_ZH ? '请指定对应的首尾纹理' : ' (Please set the head/tail texture)');
  830. return;
  831. }
  832. let headNode = this.node.getChildByName('_headNode_');
  833. if (!headNode) {
  834. headNode = new Node('_headNode_');
  835. headNode.setParent(this.node);
  836. }
  837. headNode.setSiblingIndex(0);
  838. headNode.setPosition(0, 0, this.node.z);
  839. // headNode.setScale(this.node.scale.x, this.node.scale.y, 1);
  840. let meshR = headNode.getComponent(MeshRenderer);
  841. if (!meshR) {
  842. meshR = headNode.addComponent(MeshRenderer);
  843. }
  844. // let uiMeshR = this.node.getComponent(UIMeshRenderer);
  845. // if (!uiMeshR) {
  846. // headNode.getComponent(MeshRenderer)?.destroy();
  847. // } else {
  848. // if (!headNode.getComponent(UIMeshRenderer)) {
  849. // headNode.addComponent(UIMeshRenderer);
  850. // }
  851. // }
  852. let uiMeshR = headNode.getComponent(UIMeshRenderer);
  853. if (!uiMeshR) {
  854. headNode.addComponent(UIMeshRenderer);
  855. }
  856. let mat = meshR.getMaterialInstance(0) as Material;
  857. if (!mat) {
  858. mat = new Material();
  859. mat.initialize({
  860. effectAsset: this._spEffectAsset,
  861. defines: {
  862. USE_TEXTURE: true,
  863. },
  864. });
  865. meshR.setMaterialInstance(mat, 0);
  866. }
  867. mat.setProperty('mainTexture', this._headTailFrame.texture, 0);
  868. const uv = this._headTailFrame.uv;
  869. const uv_x = uv[0]; // ul
  870. const uv_y = uv[1]; // vb
  871. const uv_w = uv[6] - uv[0]; // ur - ul
  872. const uv_h = uv[5] - uv[1]; // vt - vb
  873. const altlasUV = v4(uv_x, uv_y, uv_w, uv_h);
  874. // console.log('altlasUV', altlasUV);
  875. mat.setProperty('altlasUV', altlasUV, 0);
  876. const cur = this._vertexesSegment[0]; //.clone().multiply(this.node.scale);
  877. const next = this._vertexesSegment[1]; //.clone().multiply(this.node.scale);
  878. let p2 = cur.clone();
  879. let p3 = this._getTangentPos(p2, next, -Math.PI / 2, this._curve_height_result);
  880. let p0 = cur.clone().subtract(next).normalize().multiplyScalar(this._headTailFrame.originalSize.width).add(cur);
  881. let p1 = p3.clone().subtract(p2).add(p0);
  882. if (this._renderMode === TerrainRenderMode.TANGENT_MODE_CENTER) {
  883. const vAdd: Vec3 = p0.clone().subtract(p1).multiplyScalar(0.5);
  884. p0 = p0.add(vAdd);
  885. p1 = p1.add(vAdd);
  886. p2 = p2.add(vAdd);
  887. p3 = p3.add(vAdd);
  888. }
  889. let posset: number[] = [];
  890. let tmp: Vec3[] = [];
  891. tmp.push(p0, p1, p2, p3);
  892. for (let i = 0; i < tmp.length; i++) {
  893. posset.push(tmp[i].x, tmp[i].y, tmp[i].z);
  894. }
  895. let uvs = [0, 0, 0, 1, 1, 0, 1, 1];
  896. let indices = this.node.scale.x * this.node.scale.y > 0 ? [0, 1, 2, 1, 3, 2] : [0, 2, 1, 1, 2, 3];
  897. // let indices = [0, 1, 2, 1, 3, 2];
  898. if (headNode.scale.x * headNode.scale.y < 0) {
  899. indices = [0, 2, 1, 1, 2, 3];
  900. }
  901. let mesh = utils.MeshUtils.createMesh({
  902. positions: posset,
  903. uvs: uvs,
  904. indices: indices,
  905. });
  906. if (meshR!.mesh) {
  907. meshR.mesh!.destroy();
  908. }
  909. meshR.mesh = mesh;
  910. } else {
  911. let headNode = this.node.getChildByName('_headNode_');
  912. if (headNode) {
  913. headNode.destroy();
  914. }
  915. }
  916. }
  917. private _updateTail() {
  918. if (this._headTailFixedOnOff) {
  919. if (!this._headTailFrame) {
  920. console.warn(IN_EDITOR_ZH ? '请指定对应的首尾纹理' : ' (Please set the head/tail texture)');
  921. return;
  922. }
  923. let tailNode = this.node.getChildByName('_tailNode_');
  924. if (!tailNode) {
  925. tailNode = new Node('_tailNode_');
  926. tailNode.setParent(this.node);
  927. }
  928. tailNode.setSiblingIndex(0);
  929. tailNode.setPosition(0, 0, this.node.z);
  930. // tailNode.setScale(this.node.scale.x, this.node.scale.y, 1);
  931. let meshR = tailNode.getComponent(MeshRenderer);
  932. if (!meshR) {
  933. meshR = tailNode.addComponent(MeshRenderer);
  934. }
  935. // let uiMeshR = this.node.getComponent(UIMeshRenderer);
  936. // if (!uiMeshR) {
  937. // tailNode.getComponent(MeshRenderer)?.destroy();
  938. // } else {
  939. // if (!tailNode.getComponent(UIMeshRenderer)) {
  940. // tailNode.addComponent(UIMeshRenderer);
  941. // }
  942. // }
  943. let uiMeshR = tailNode.getComponent(UIMeshRenderer);
  944. if (!uiMeshR) {
  945. tailNode.addComponent(UIMeshRenderer);
  946. }
  947. let mat = meshR.getMaterialInstance(0) as Material;
  948. if (!mat) {
  949. mat = new Material();
  950. mat.initialize({
  951. effectAsset: this._spEffectAsset,
  952. defines: {
  953. USE_TEXTURE: true,
  954. },
  955. });
  956. // this._headTailFrame.texture.setWrapMode(
  957. // Texture2D.WrapMode.REPEAT,
  958. // Texture2D.WrapMode.REPEAT,
  959. // Texture2D.WrapMode.REPEAT
  960. // );
  961. meshR.setMaterialInstance(mat, 0);
  962. }
  963. mat.setProperty('mainTexture', this._headTailFrame.texture, 0);
  964. const uv = this._headTailFrame.uv;
  965. const uv_x = uv[0]; // ul
  966. const uv_y = uv[1]; // vb
  967. const uv_w = uv[6] - uv[0]; // ur - ul
  968. const uv_h = uv[5] - uv[1]; // vt - vb
  969. const altlasUV = v4(uv_x, uv_y, uv_w, uv_h);
  970. // console.log('altlasUV', altlasUV);
  971. mat.setProperty('altlasUV', altlasUV, 0);
  972. let p0 = this._vertexesSegment[this._vertexesSegment.length - 1].clone();
  973. let p1 = this._getTangentPos(
  974. p0,
  975. this._vertexesSegment[this._vertexesSegment.length - 2],
  976. Math.PI / 2,
  977. this._curve_height_result
  978. );
  979. let p2 = p0
  980. .clone()
  981. .subtract(this._vertexesSegment[this._vertexesSegment.length - 2])
  982. .normalize()
  983. .multiplyScalar(this._headTailFrame.originalSize.width)
  984. .add(p0);
  985. let p3 = p2.clone().subtract(p0).add(p1);
  986. if (this._renderMode === TerrainRenderMode.TANGENT_MODE_CENTER) {
  987. const vAdd: Vec3 = p0.clone().subtract(p1).multiplyScalar(0.5);
  988. p0 = p0.add(vAdd);
  989. p1 = p1.add(vAdd);
  990. p2 = p2.add(vAdd);
  991. p3 = p3.add(vAdd);
  992. }
  993. let posset: number[] = [];
  994. let tmp: Vec3[] = [];
  995. tmp.push(p0, p1, p2, p3);
  996. for (let i = 0; i < tmp.length; i++) {
  997. posset.push(tmp[i].x, tmp[i].y, tmp[i].z);
  998. }
  999. let uvs = [1, 0, 1, 1, 0, 0, 0, 1];
  1000. let indices = this.node.scale.x * this.node.scale.y > 0 ? [0, 1, 2, 1, 3, 2] : [0, 2, 1, 1, 2, 3];
  1001. let mesh = utils.MeshUtils.createMesh({
  1002. positions: posset,
  1003. uvs: uvs,
  1004. indices: indices,
  1005. });
  1006. if (meshR!.mesh) {
  1007. meshR.mesh!.destroy();
  1008. }
  1009. meshR.mesh = mesh;
  1010. } else {
  1011. let headNode = this.node.getChildByName('_tailNode_');
  1012. if (headNode) {
  1013. headNode.destroy();
  1014. }
  1015. }
  1016. }
  1017. //细分控制点,通过Catmull-Rom 样条曲线来插值平滑 'Subdivide control points, interpolate and smooth using Catmull-Rom spline'
  1018. private _catmullRom(p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, t: number): Vec3 {
  1019. const t2 = t * t;
  1020. const t3 = t2 * t;
  1021. // Catmull-Rom spline formula for 3D
  1022. const x =
  1023. 0.5 *
  1024. (2 * p1.x +
  1025. (-p0.x + p2.x) * t +
  1026. (2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x) * t2 +
  1027. (-p0.x + 3 * p1.x - 3 * p2.x + p3.x) * t3);
  1028. const y =
  1029. 0.5 *
  1030. (2 * p1.y +
  1031. (-p0.y + p2.y) * t +
  1032. (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
  1033. (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
  1034. // const z =
  1035. // 0.5 *
  1036. // (2 * p1.z +
  1037. // (-p0.z + p2.z) * t +
  1038. // (2 * p0.z - 5 * p1.z + 4 * p2.z - p3.z) * t2 +
  1039. // (-p0.z + 3 * p1.z - 3 * p2.z + p3.z) * t3);
  1040. return v3(x, y, 0);
  1041. }
  1042. //最小分段距离 'Minimum segment length'
  1043. private _minSegmentLength: number = 8;
  1044. private _generateSegmentVertex(seg_count: number = 8, physics: boolean = false): Vec3[] {
  1045. const _seg: Vec3[] = [];
  1046. // this._vertexesSegment.length = 0;
  1047. const points = this._controlPoints;
  1048. const pointCount = points.length;
  1049. if (0 === seg_count) {
  1050. _seg.push(...points);
  1051. physics ||
  1052. console.log(
  1053. this.node.name + (IN_EDITOR_ZH ? ' 细分后点数量为:' : ' (Number of points after subdivision):'),
  1054. _seg.length
  1055. );
  1056. if (this._headTailCloseOnoff) {
  1057. _seg.push(points[0].clone());
  1058. }
  1059. return _seg;
  1060. }
  1061. if (pointCount < 2) {
  1062. if (pointCount === 1) {
  1063. _seg.push(points[0].clone());
  1064. }
  1065. return _seg;
  1066. }
  1067. if (pointCount === 2) {
  1068. const p0 = points[0];
  1069. const p1 = points[1];
  1070. _seg.push(p0.clone());
  1071. _seg.push(p1.clone());
  1072. return _seg;
  1073. }
  1074. // Catmull-Rom样条 'Catmull-Rom spline'
  1075. const paddedPoints: Vec3[] = [];
  1076. let interCounts = 0;
  1077. if (this._headTailCloseOnoff) {
  1078. paddedPoints.push(points[points.length - 1].clone());
  1079. paddedPoints.push(...points);
  1080. paddedPoints.push(points[0].clone());
  1081. paddedPoints.push(points[1].clone());
  1082. interCounts = pointCount;
  1083. } else {
  1084. interCounts = pointCount - 1;
  1085. paddedPoints.push(points[0].clone().add(points[0].clone().subtract(points[1])));
  1086. paddedPoints.push(...points);
  1087. paddedPoints.push(
  1088. points[pointCount - 1].clone().add(points[pointCount - 1].clone().subtract(points[pointCount - 2]))
  1089. );
  1090. }
  1091. for (let i = 0; i < interCounts; i++) {
  1092. const p0 = paddedPoints[i];
  1093. const p1 = paddedPoints[i + 1];
  1094. const p2 = paddedPoints[i + 2];
  1095. const p3 = paddedPoints[i + 3];
  1096. if (i === 0) {
  1097. _seg.push(p1.clone());
  1098. }
  1099. // 计算该段的长度(用p1-p2的距离近似) 'Calculate the length of this segment (approximated by the distance between p1 and p2)'
  1100. const dist = p1.clone().subtract(p2).length();
  1101. let seg = seg_count;
  1102. // 如果距离小于最小分段距离,则不分段,直接连接
  1103. if (dist < 40) {
  1104. seg = 1;
  1105. } else {
  1106. while (seg > 1 && dist / seg < this._minSegmentLength) {
  1107. seg = Math.floor(seg / 2);
  1108. }
  1109. }
  1110. const step = 1 / seg;
  1111. for (let j = 1; j <= seg; j++) {
  1112. const t = j * step;
  1113. const interpolatedPoint = this._catmullRom(p0, p1, p2, p3, t);
  1114. _seg.push(interpolatedPoint);
  1115. }
  1116. }
  1117. // if (this._vertexesSegment.length > 0) {
  1118. // const lastOriginalPoint = points[pointCount - 1];
  1119. // this._vertexesSegment[this._vertexesSegment.length - 1] = lastOriginalPoint.clone();
  1120. // }
  1121. // // 需要在不同模式下分别考虑
  1122. // for (let i = this._vertexesSegment.length - 1; i > 0; i--) {
  1123. // // if (this._vertexesSegment[i].equals(this._vertexesSegment[i - 1], 2)) {
  1124. // if (isCloseToVec3(this._vertexesSegment[i], this._vertexesSegment[i - 1], 2)) {
  1125. // this._vertexesSegment.splice(i, 1);
  1126. // }
  1127. // }
  1128. // this._vertexesSegment = this._vertexesSegment.filter(
  1129. // (v) => isFinite(v.x) && isFinite(v.y) && (v.x !== 0 || v.y !== 0)
  1130. // );
  1131. physics ||
  1132. console.log(
  1133. this.node.name + (IN_EDITOR_ZH ? ' 细分后点数量为:' : ' (Number of points after subdivision):'),
  1134. _seg.length
  1135. );
  1136. return _seg;
  1137. }
  1138. private _updateMesh() {
  1139. if (!this._spFrame || this._controlPoints.length < 2) {
  1140. console.warn(IN_EDITOR_ZH ? '至少需要两个控制点' : 'Need at least 2 points to define the curve.');
  1141. this.renderer!.mesh = null;
  1142. return;
  1143. }
  1144. const positions = this._calculateVertexes();
  1145. if (positions.length === 0) {
  1146. console.warn(this.node.name, IN_EDITOR_ZH ? '顶点数据数量为0' : 'No vertexes calculated.');
  1147. this.renderer!.mesh = null;
  1148. return;
  1149. }
  1150. const uvs =
  1151. this._renderUVMode == TerrainRenderUVMode.UV_REPEAT_GRID ? this._calculateUVs() : this._calculateTangentsUVs();
  1152. // const uvs = this._calculateUVs();
  1153. const indices = this._calculateIndices();
  1154. // const normals = this._calculateNormals();
  1155. // console.log('CurveTexture.ts _updateMesh pos ', positions);
  1156. // console.log('CurveTexture.ts _updateMesh uvs ', uvs);
  1157. // console.log('CurveTexture.ts _updateMesh indices ', indices);
  1158. // console.log('CurveTexture.ts _updateMesh normals ', normals);
  1159. let mesh = utils.MeshUtils.createMesh({
  1160. positions: positions,
  1161. uvs: uvs,
  1162. indices: indices,
  1163. // normals: normals,
  1164. });
  1165. // console.log('CurveTexture.ts _updateMesh positions ', positions);
  1166. // console.log('CurveTexture.ts _updateMesh uvs ', uvs);
  1167. // console.log('CurveTexture.ts _updateMesh indices ', indices);
  1168. if (this.renderer!.mesh) {
  1169. this.renderer!.mesh.destroy();
  1170. }
  1171. this.renderer!.mesh = mesh;
  1172. }
  1173. /**
  1174. * 计算两条线段的交点
  1175. * @param p1 第一条线段的起点
  1176. * @param p2 第一条线段的终点
  1177. * @param p3 第二条线段的起点
  1178. * @param p4 第二条线段的终点
  1179. * @param extended 是否考虑延长线,默认false
  1180. * @returns 交点(Vec2),如果没有交点则返回 false
  1181. */
  1182. private _crossPointBy2Segment(
  1183. p1: Vec2 | Vec3,
  1184. p2: Vec2 | Vec3,
  1185. p3: Vec2 | Vec3,
  1186. p4: Vec2 | Vec3,
  1187. extended: boolean = false
  1188. ): Vec2 {
  1189. const a = p1,
  1190. b = p2; // 第一条线段: a->b
  1191. const c = p3,
  1192. d = p4; // 第二条线段: c->d
  1193. // 计算分母 'Calculate denominator'
  1194. const denominator = (d.y - c.y) * (b.x - a.x) - (d.x - c.x) * (b.y - a.y);
  1195. if (denominator === 0) return null; // 平行或共线 'Parallel or collinear'
  1196. // 计算 ua 和 ub 'Calculate ua and ub'
  1197. const ua = ((d.x - c.x) * (a.y - c.y) - (d.y - c.y) * (a.x - c.x)) / denominator;
  1198. const ub = ((b.x - a.x) * (a.y - c.y) - (b.y - a.y) * (a.x - c.x)) / denominator;
  1199. if (extended) {
  1200. return v2(a.x + ua * (b.x - a.x), a.y + ua * (b.y - a.y));
  1201. }
  1202. // 检查交点是否在线段上 'Check if the intersection point is on the segment'
  1203. if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
  1204. return v2(a.x + ua * (b.x - a.x), a.y + ua * (b.y - a.y));
  1205. }
  1206. return null;
  1207. }
  1208. /**
  1209. * 计算从向量a到向量b的有符号夹角
  1210. * @param a 起始向量
  1211. * @param b 目标向量
  1212. * @param normal 参考法向量(决定正方向),默认(0,0,1)
  1213. * @returns 夹角(范围:-π ~ π),零向量返回NaN
  1214. */
  1215. private _signedAngle3D(a: Vec3, b: Vec3, normal?: Vec3): number {
  1216. // 检查零向量
  1217. const magA = Math.sqrt(a.x ** 2 + a.y ** 2 + a.z ** 2);
  1218. const magB = Math.sqrt(b.x ** 2 + b.y ** 2 + b.z ** 2);
  1219. if (magA === 0 || magB === 0) return NaN;
  1220. // 计算点积和叉积
  1221. const dot = a.x * b.x + a.y * b.y + a.z * b.z;
  1222. const cross = {
  1223. x: a.y * b.z - a.z * b.y,
  1224. y: a.z * b.x - a.x * b.z,
  1225. z: a.x * b.y - a.y * b.x,
  1226. };
  1227. const nor = normal ? normal : v3(0, 0, 1);
  1228. // 叉积与法向量的点积决定方向
  1229. const crossDotNormal = cross.x * nor.x + cross.y * nor.y + cross.z * nor.z;
  1230. const sign = Math.sign(crossDotNormal);
  1231. // 计算无符号夹角
  1232. const cosTheta = Math.min(1, Math.max(-1, dot / (magA * magB)));
  1233. const unsignedAngle = Math.acos(cosTheta);
  1234. return sign * unsignedAngle;
  1235. }
  1236. //根据一组控制点和一个厚度,计算TANGENT_MODE模式下的顺时针90°顶点
  1237. private _calclateTangentPoints(indata: Vec3[], thickness: number): Vec3[] {
  1238. thickness = thickness < 0 ? 1 : thickness;
  1239. let pCurrentClockLast = new Vec2(0, 0);
  1240. let pNextClockLast = new Vec2(0, 0);
  1241. let outPoints: Vec3[] = [];
  1242. let temp = indata;
  1243. if (this._headTailCloseOnoff) {
  1244. temp = indata.map((v) => v.clone());
  1245. temp.push(temp[1].clone());
  1246. }
  1247. for (let index = 0; index < temp.length; index++) {
  1248. outPoints.push(temp[index].clone());
  1249. //使用下半距离平行线交点方式来取点
  1250. const thick = thickness * 1.0;
  1251. let directVec = new Vec3(0, 0, 0);
  1252. let directNormalVec = new Vec2(0, 0);
  1253. if (index == temp.length - 1) {
  1254. directVec = temp[index].clone().subtract(temp[index - 1]);
  1255. directNormalVec = directVec.clone().normalize().toVec2();
  1256. } else {
  1257. directVec = temp[index + 1].clone().subtract(temp[index]);
  1258. directNormalVec = directVec.clone().normalize().toVec2();
  1259. }
  1260. let clockwisePoint = directNormalVec
  1261. .clone()
  1262. .rotate(-Math.PI / 2)
  1263. .multiplyScalar(thick);
  1264. let pCurrentClock = temp[index].toVec2().add(clockwisePoint);
  1265. if (index < temp.length - 1) {
  1266. let pNextClock = temp[index + 1].toVec2().add(clockwisePoint);
  1267. if (index > 0) {
  1268. const lenStand = 1.414 * thick;
  1269. //计算顺时针交点
  1270. let crossPointClock = this._crossPointBy2Segment(
  1271. pCurrentClockLast,
  1272. pNextClockLast,
  1273. pCurrentClock,
  1274. pNextClock,
  1275. true
  1276. );
  1277. if (crossPointClock) {
  1278. const vDir1 = crossPointClock.clone().subtract(temp[index].toVec2());
  1279. const vDirNormal1 = vDir1.clone().normalize();
  1280. if (vDir1.length() > lenStand && this._smoothness > 6) {
  1281. //以前后向量垂直模式下为长度标准
  1282. crossPointClock = vDirNormal1.clone().multiplyScalar(lenStand).add(temp[index].toVec2());
  1283. // console.log('crossPointClock 太远,截断重置', crossPointClock);
  1284. }
  1285. outPoints.push(crossPointClock.toVec3());
  1286. } else {
  1287. console.log(`${this.node.name} 交点计算失败,交点退化为原始点`);
  1288. outPoints.push(pCurrentClock.toVec3());
  1289. }
  1290. }
  1291. pNextClockLast = pNextClock;
  1292. }
  1293. if (index === 0 || index === temp.length - 1) {
  1294. outPoints.push(pCurrentClock.toVec3());
  1295. }
  1296. pCurrentClockLast = pCurrentClock;
  1297. }
  1298. if (this._headTailCloseOnoff) {
  1299. outPoints.pop();
  1300. outPoints.pop();
  1301. outPoints[0] = outPoints[outPoints.length - 2];
  1302. outPoints[1] = outPoints[outPoints.length - 1];
  1303. }
  1304. //对一些异常情况处理,如果控制点和顺时针点的连线和任何一段控制点线段相交,则这个顺时针点修改为这个交点
  1305. const outPointsVec2 = outPoints.map((v) => v.toVec2());
  1306. for (let index = 0; index < outPointsVec2.length; index += 2) {
  1307. if (0 === index || outPointsVec2.length - 2 === index) continue;
  1308. const pCur = outPointsVec2[index];
  1309. const pCurClock = outPointsVec2[index + 1];
  1310. for (let j = 0; j < outPointsVec2.length; j += 2) {
  1311. if (outPointsVec2.length - 2 === j) continue;
  1312. if (j === index || j === index - 2 || j === index + 2) continue;
  1313. const pSeg0 = outPointsVec2[j];
  1314. const pSeg1 = outPointsVec2[j + 2];
  1315. const r = this._crossPointBy2Segment(pCur, pCurClock, pSeg0, pSeg1, false);
  1316. if (r) {
  1317. outPoints[index + 1].x = r.x;
  1318. outPoints[index + 1].y = r.y;
  1319. // console.log(`检测到索引为${index}的点的顺时针顶点异常,已修正为交点`, r);
  1320. }
  1321. }
  1322. }
  1323. return outPoints;
  1324. }
  1325. //根据一组控制点和一个厚度,计算TANGENT_MODE_CENTER模式下的上下顶点
  1326. private _calclateTangentCenterPoints(indata: Vec3[], thickness: number, physics: boolean = false): Vec3[] {
  1327. thickness = thickness < 0 ? 1 : thickness;
  1328. let pCurrentAnticlockLast = new Vec2(0, 0);
  1329. let pCurrentClockLast = new Vec2(0, 0);
  1330. let pNextAnticlockLast = new Vec2(0, 0);
  1331. let pNextClockLast = new Vec2(0, 0);
  1332. let outPoints: Vec3[] = [];
  1333. let temp = indata;
  1334. if (this._headTailCloseOnoff) {
  1335. temp = indata.map((v) => v.clone());
  1336. temp.push(temp[1].clone());
  1337. }
  1338. for (let index = 0; index < temp.length; index++) {
  1339. //使用上下半距离平行线交点方式来取点
  1340. const halfH = thickness * 0.5;
  1341. let directVec = new Vec3(0, 0, 0);
  1342. let directNormalVec = new Vec2(0, 0);
  1343. if (index == temp.length - 1) {
  1344. directVec = temp[index].clone().subtract(temp[index - 1]);
  1345. directNormalVec = directVec.clone().normalize().toVec2();
  1346. } else {
  1347. directVec = temp[index + 1].clone().subtract(temp[index]);
  1348. directNormalVec = directVec.clone().normalize().toVec2();
  1349. }
  1350. let antiClockwisePoint = directNormalVec
  1351. .clone()
  1352. .rotate(Math.PI / 2)
  1353. .multiplyScalar(halfH);
  1354. let clockwisePoint = directNormalVec
  1355. .clone()
  1356. .rotate(-Math.PI / 2)
  1357. .multiplyScalar(halfH);
  1358. let pCurrentAnticlock = temp[index].toVec2().add(antiClockwisePoint);
  1359. let pCurrentClock = temp[index].toVec2().add(clockwisePoint);
  1360. if (index < temp.length - 1) {
  1361. let pNextAnticlock = temp[index + 1].toVec2().add(antiClockwisePoint);
  1362. let pNextClock = temp[index + 1].toVec2().add(clockwisePoint);
  1363. if (index > 0) {
  1364. //计算逆时针交点
  1365. let crossPointAnticlock = this._crossPointBy2Segment(
  1366. pCurrentAnticlockLast,
  1367. pNextAnticlockLast,
  1368. pCurrentAnticlock,
  1369. pNextAnticlock,
  1370. true
  1371. );
  1372. const lenStand = 1.414 * halfH;
  1373. if (crossPointAnticlock) {
  1374. const vDir = crossPointAnticlock.clone().subtract(temp[index].toVec2());
  1375. const vDirNormal = vDir.clone().normalize();
  1376. if (vDir.length() > lenStand && this._smoothness > 6) {
  1377. //以前后向量垂直模式下为长度标准
  1378. crossPointAnticlock = vDirNormal.clone().multiplyScalar(lenStand).add(temp[index].toVec2());
  1379. // console.log('crossPointAnticlock 太远,截断重置', crossPointAnticlock);
  1380. }
  1381. outPoints.push(crossPointAnticlock.toVec3());
  1382. } else {
  1383. console.log(`${this.node.name} 交点计算失败,交点退化为原始点`);
  1384. outPoints.push(pCurrentAnticlock.toVec3());
  1385. }
  1386. //计算顺时针交点
  1387. let crossPointClock = this._crossPointBy2Segment(
  1388. pCurrentClockLast,
  1389. pNextClockLast,
  1390. pCurrentClock,
  1391. pNextClock,
  1392. true
  1393. );
  1394. if (crossPointClock) {
  1395. const vDir1 = crossPointClock.clone().subtract(temp[index].toVec2());
  1396. const vDirNormal1 = vDir1.clone().normalize();
  1397. if (vDir1.length() > lenStand && this._smoothness > 6) {
  1398. //以前后向量垂直模式下为长度标准
  1399. crossPointClock = vDirNormal1.clone().multiplyScalar(lenStand).add(temp[index].toVec2());
  1400. // console.log('crossPointClock 太远,截断重置', crossPointClock);
  1401. }
  1402. outPoints.push(crossPointClock.toVec3());
  1403. } else {
  1404. console.log(`${this.node.name} 交点计算失败,交点退化为原始点`);
  1405. outPoints.push(pCurrentClock.toVec3());
  1406. }
  1407. }
  1408. pNextAnticlockLast = pNextAnticlock;
  1409. pNextClockLast = pNextClock;
  1410. }
  1411. if (index === 0) {
  1412. outPoints.push(pCurrentAnticlock.toVec3());
  1413. outPoints.push(pCurrentClock.toVec3());
  1414. }
  1415. if (index === temp.length - 1) {
  1416. outPoints.push(pCurrentAnticlock.toVec3());
  1417. outPoints.push(pCurrentClock.toVec3());
  1418. }
  1419. pCurrentAnticlockLast = pCurrentAnticlock;
  1420. pCurrentClockLast = pCurrentClock;
  1421. }
  1422. if (this._headTailCloseOnoff) {
  1423. outPoints.pop();
  1424. outPoints.pop();
  1425. outPoints[0] = outPoints[outPoints.length - 2];
  1426. outPoints[1] = outPoints[outPoints.length - 1];
  1427. }
  1428. // if (physics) {
  1429. // const evenPoints = outPoints.filter((_, idx) => idx % 2 === 0);
  1430. // const end = evenPoints.pop();
  1431. // console.log(`${this.node.name} evenPoints 1 :`, evenPoints);
  1432. // this._openedInsectLinesWipe(evenPoints);
  1433. // console.log(`${this.node.name} evenPoints 2 :`, evenPoints);
  1434. // const tmpPoints: Vec3[] = [];
  1435. // if (evenPoints.length > 12) {
  1436. // tmpPoints.push(evenPoints[evenPoints.length - 3]);
  1437. // tmpPoints.push(evenPoints[evenPoints.length - 2]);
  1438. // tmpPoints.push(evenPoints[evenPoints.length - 1]);
  1439. // tmpPoints.push(end);
  1440. // tmpPoints.push(evenPoints[0]);
  1441. // tmpPoints.push(evenPoints[1]);
  1442. // tmpPoints.push(evenPoints[2]);
  1443. // this._openedInsectLinesWipe(tmpPoints);
  1444. // console.log(`${this.node.name} evenPoints 3 :`, evenPoints);
  1445. // }
  1446. // }
  1447. return outPoints;
  1448. }
  1449. //处理一段不闭合的连续点的自相交
  1450. // private _openedInsectLinesWipe(evenPoints: Vec2[] | Vec3[]) {
  1451. // for (let i = 0; i < evenPoints.length; i++) {
  1452. // const a = evenPoints[i];
  1453. // const b = evenPoints[i + 1];
  1454. // for (let j = i + 2; j < evenPoints.length - 1; j++) {
  1455. // const c = evenPoints[j];
  1456. // const d = evenPoints[j + 1];
  1457. // const p = this._crossPointBy2Segment(a, b, c, d, false);
  1458. // if (p) {
  1459. // // 替换B到C区间的点为交点P
  1460. // // for (let k = i + 1; k <= j; k++) {
  1461. // // evenPoints[k].x = p.x;
  1462. // // evenPoints[k].y = p.y;
  1463. // // }
  1464. // d.x = c.x;
  1465. // d.y = c.y;
  1466. // // 只处理一次交点,跳出
  1467. // // break;
  1468. // }
  1469. // }
  1470. // }
  1471. // }
  1472. private _calculateVertexes() {
  1473. let positions: number[] = [];
  1474. this._vertexes.length = 0;
  1475. if (this._vertexesSegment.length < 2) {
  1476. console.warn(this.node.name, IN_EDITOR_ZH ? '细分点数量小于2' : 'count of subdivided points is less than 2');
  1477. return positions;
  1478. }
  1479. let temp: Vec3[] = [];
  1480. for (let i = 0; i < this._vertexesSegment.length; i++) {
  1481. const v = this._vertexesSegment[i];
  1482. if (v) {
  1483. temp.push(v.clone().add(this._offset));
  1484. }
  1485. }
  1486. if (this._renderMode == TerrainRenderMode.VERTICAL_MODE) {
  1487. for (let index = 0; index < temp.length - 1; index++) {
  1488. const item1 = temp[index];
  1489. const item2 = temp[index + 1];
  1490. const item1_bottom = item1.clone().add(v3(0, -this._curve_height_result, 0));
  1491. const item2_bottom = item2.clone().add(v3(0, -this._curve_height_result, 0));
  1492. this._vertexes.push(item1);
  1493. this._vertexes.push(item1_bottom);
  1494. this._vertexes.push(item2);
  1495. this._vertexes.push(item1_bottom);
  1496. this._vertexes.push(item2_bottom);
  1497. this._vertexes.push(item2);
  1498. }
  1499. } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE) {
  1500. this._vertexes = this._calclateTangentPoints(temp, this._curve_height_result);
  1501. } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE_CENTER) {
  1502. this._vertexes = this._calclateTangentCenterPoints(temp, this._curve_height_result);
  1503. }
  1504. // console.log(this._vertexes);
  1505. this._vertexes.forEach((item) => {
  1506. positions.push(item.x, item.y, item.z);
  1507. });
  1508. return positions;
  1509. }
  1510. private _calculateUVs() {
  1511. let uvs: number[] = [];
  1512. if (!this._spFrame || this._vertexes.length === 0 || this._vertexesSegment.length < 2) {
  1513. return uvs;
  1514. }
  1515. for (let i = 0; i < this._vertexes.length; i++) {
  1516. const d = this._vertexes[i];
  1517. const u = d.x / this._curve_width;
  1518. let v = 1.0 - d.y / this._curve_height;
  1519. if (this._updownFix) {
  1520. v = d.y / this._curve_height;
  1521. }
  1522. uvs.push(u, v);
  1523. }
  1524. return uvs;
  1525. }
  1526. private _calculateTangentsUVs() {
  1527. let uvs: number[] = [];
  1528. const textureWidth = this._curve_width;
  1529. const textureHeight = this._curve_height;
  1530. const textureSegmentLength = textureWidth;
  1531. let index = 0;
  1532. let last_pos_up: Vec3 | null = null;
  1533. let length_all_up = 0;
  1534. let current_top_u = 0;
  1535. for (const pt of this._vertexes) {
  1536. let u = 0;
  1537. let v = 0;
  1538. if (index % 2 == 0) {
  1539. v = this._updownFix ? this._curve_height_result / textureHeight : 0;
  1540. if (last_pos_up != null) {
  1541. length_all_up += pt.clone().subtract(last_pos_up).length();
  1542. // 直接计算重复的纹理坐标
  1543. u = length_all_up / textureSegmentLength;
  1544. } else {
  1545. u = 0;
  1546. }
  1547. current_top_u = u;
  1548. last_pos_up = pt;
  1549. } else {
  1550. v = this._updownFix ? 0 : this._curve_height_result / textureHeight;
  1551. u = current_top_u;
  1552. }
  1553. uvs.push(u, v);
  1554. index += 1;
  1555. }
  1556. return uvs;
  1557. }
  1558. private _calculateNormals() {
  1559. let normals: number[] = [];
  1560. const normal = v3(0, 0, 1);
  1561. for (let i = 0; i < this._vertexes.length; i++) {
  1562. normals.push(normal.x, normal.y, normal.z);
  1563. }
  1564. return normals;
  1565. }
  1566. private _calculateIndices() {
  1567. const indices: number[] = [];
  1568. const vertexCount = this._vertexes.length;
  1569. for (let i = 0; i < vertexCount - 2; i++) {
  1570. if (this.node.scale.x * this.node.scale.y > 0) {
  1571. indices.push(i);
  1572. indices.push(i % 2 == 0 ? i + 1 : i + 2);
  1573. indices.push(i % 2 == 0 ? i + 2 : i + 1);
  1574. } else {
  1575. indices.push(i);
  1576. indices.push(i % 2 == 0 ? i + 2 : i + 1);
  1577. indices.push(i % 2 == 0 ? i + 1 : i + 2);
  1578. }
  1579. }
  1580. return indices;
  1581. }
  1582. //-------------------提取物理多边形点------------------------ 'Extract physical polygon points'
  1583. private _extractPolygonOutline(indata: Vec3[]) {
  1584. if (this._renderMode == TerrainRenderMode.VERTICAL_MODE) {
  1585. const points: Vec2[] = [];
  1586. let d = this._vertexesSegment.map((item) => item.clone().add(this._offsetCollider).toVec2());
  1587. points.push(...d);
  1588. let down = d.map((item) => item.clone().add(v2(0, -this._curve_collider_thickness))).reverse();
  1589. points.push(...down);
  1590. return points;
  1591. } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE_CENTER) {
  1592. //切线模式2
  1593. let d = this._vertexesSegment.map((item) => item.clone().add(this._offsetCollider));
  1594. let verts = this._calclateTangentCenterPoints(d, this._curve_collider_thickness, true);
  1595. //特殊处理闭合
  1596. // if (this._headTailCloseOnoff) {
  1597. //这里计算物理多边形的逻辑有点绕,需要先确认按照正常的细分点得到的结果是否自交,如果自交,丢弃,不处理(尤其是凹多边形的自交和裁剪是个大问题).
  1598. //如果自交,则直接退化到细分为1 的结果进行计算,细分为1的结果确定不会自交.
  1599. let inner = verts.filter((_, idx) => idx % 2 === 0);
  1600. let smoothness = this._smoothness;
  1601. while (UsefulTools.isPolygonSelfIntersect(inner).result && smoothness > 2) {
  1602. smoothness -= 2;
  1603. const _seg: Vec3[] = this._generateSegmentVertex(smoothness, true);
  1604. verts = this._calclateTangentCenterPoints(_seg, this._curve_collider_thickness, true);
  1605. inner = verts.filter((_, idx) => idx % 2 === 0);
  1606. }
  1607. // }
  1608. let pointsAnticlock: Vec2[] = [];
  1609. let pointsClock: Vec2[] = [];
  1610. for (let i = 0; i < verts.length; i++) {
  1611. if (i % 2 == 0) {
  1612. pointsAnticlock.push(verts[i].clone().add(this._offsetCollider).toVec2());
  1613. } else {
  1614. pointsClock.push(verts[i].clone().add(this._offsetCollider).toVec2());
  1615. }
  1616. }
  1617. return pointsAnticlock.concat(pointsClock.reverse());
  1618. } else if (this._renderMode == TerrainRenderMode.TANGENT_MODE) {
  1619. let d = this._vertexesSegment.map((item) => item.clone().add(this._offsetCollider));
  1620. let verts = this._calclateTangentPoints(d, this._curve_collider_thickness);
  1621. // if (this._headTailCloseOnoff) {
  1622. let inner = verts.filter((_, idx) => idx % 2 === 0);
  1623. let smoothness = this._smoothness;
  1624. while (UsefulTools.isPolygonSelfIntersect(inner).result && smoothness > 2) {
  1625. smoothness -= 2;
  1626. const _seg: Vec3[] = this._generateSegmentVertex(smoothness, true);
  1627. verts = this._calclateTangentCenterPoints(_seg, this._curve_collider_thickness, true);
  1628. inner = verts.filter((_, idx) => idx % 2 === 0);
  1629. }
  1630. // }
  1631. let pointsOrigin: Vec2[] = [];
  1632. let pointsClock: Vec2[] = [];
  1633. for (let i = 0; i < verts.length; i++) {
  1634. if (i % 2 == 0) {
  1635. pointsOrigin.push(verts[i].clone().add(this._offsetCollider).toVec2());
  1636. } else {
  1637. pointsClock.push(verts[i].clone().add(this._offsetCollider).toVec2());
  1638. }
  1639. }
  1640. return pointsOrigin.concat(pointsClock.reverse());
  1641. }
  1642. }
  1643. private _NAME_OF_PHYSICSNODE: string = 'physicContainerNode';
  1644. //同步更新物理组件数据 'Synchronize and update physical component data'
  1645. private generatePhysicData() {
  1646. if (!this._physcisOnOff) {
  1647. let pnode = this.node.getChildByName(this._NAME_OF_PHYSICSNODE);
  1648. if (pnode) {
  1649. pnode.destroy();
  1650. }
  1651. return;
  1652. }
  1653. //将物理组件挂在一个指定的子节点上,保证这个子节点为第一个.
  1654. let pnode = this.node.getChildByName(this._NAME_OF_PHYSICSNODE);
  1655. if (!pnode) {
  1656. pnode = new Node(this._NAME_OF_PHYSICSNODE);
  1657. pnode.setSiblingIndex(0);
  1658. this.node.addChild(pnode);
  1659. }
  1660. // pnode.x = 0;
  1661. // pnode.y = 0;
  1662. pnode.setPosition(0, 0, 0);
  1663. this._physicsCollider = pnode.getComponent(PolygonCollider2D);
  1664. if (!this._physicsCollider) {
  1665. this._physicsCollider = pnode.addComponent(PolygonCollider2D);
  1666. }
  1667. const rigid = pnode.getComponent(RigidBody2D);
  1668. if (!rigid) {
  1669. const rcomp = pnode.addComponent(RigidBody2D);
  1670. rcomp.type = ERigidBody2DType.Static;
  1671. }
  1672. // const indices = this._calculateIndices();
  1673. const polygonPoints = this._extractPolygonOutline(this._vertexes);
  1674. this._physicsCollider.points = polygonPoints;
  1675. this._physicsCollider.apply();
  1676. }
  1677. //---------------------- 编辑器中编辑功能--------------- 'Edit map in editor'
  1678. @property({})
  1679. private _visibleOnOff: boolean = true;
  1680. @property({
  1681. type: CCBoolean,
  1682. tooltip: IN_EDITOR_ZH ? '控制点显示/隐藏' : ' (Whether to display gizmo in editor)',
  1683. displayName: IN_EDITOR_ZH ? '控制点显示/隐藏' : ' (Whether to display gizmo in editor)',
  1684. group: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
  1685. })
  1686. get gizmoVisibleOnOff() {
  1687. return this._visibleOnOff;
  1688. }
  1689. set gizmoVisibleOnOff(value) {
  1690. this._editNodes.forEach((item) => {
  1691. item.active = value;
  1692. });
  1693. this._visibleOnOff = value;
  1694. }
  1695. @property({})
  1696. private _sortInXDirection: boolean = false;
  1697. @property({
  1698. type: CCBoolean,
  1699. tooltip: IN_EDITOR_ZH ? '是否在X轴方向排序' : ' (Sort in X direction)',
  1700. displayName: IN_EDITOR_ZH ? '是否在X轴方向排序' : ' (Sort in X direction)',
  1701. group: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
  1702. })
  1703. get sortInXDirection() {
  1704. return this._sortInXDirection;
  1705. }
  1706. set sortInXDirection(value) {
  1707. this._sortInXDirection = value;
  1708. }
  1709. @property({})
  1710. private _editWhenRun: boolean = false;
  1711. @property({
  1712. type: CCBoolean,
  1713. tooltip: IN_EDITOR_ZH ? '运行时存在/不存在' : 'editWhenRun',
  1714. displayName: IN_EDITOR_ZH ? '运行时存在/不存在' : 'editWhenRun',
  1715. group: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
  1716. })
  1717. get editWhenRun() {
  1718. return this._editWhenRun;
  1719. }
  1720. set editWhenRun(value) {
  1721. this._editWhenRun = value;
  1722. // this._refreshAll();
  1723. }
  1724. private _registerKeyForGizmo() {
  1725. // if (!EDITOR) return;
  1726. // input.on(Input.EventType.KEY_DOWN, (event: EventKeyboard) => {
  1727. // if (event.keyCode === KeyCode.SPACE) {
  1728. // this._showOnOff = false;
  1729. // this.showOnOff = !this.showOnOff;
  1730. // }
  1731. // });
  1732. }
  1733. @property({ type: SpriteFrame })
  1734. private _gizmotag: SpriteFrame | null = null;
  1735. @property({
  1736. type: SpriteFrame,
  1737. tooltip: IN_EDITOR_ZH ? '控制点纹理' : ' (Texture of Terrain Control Points)',
  1738. displayName: IN_EDITOR_ZH ? '控制点纹理' : ' (Texture of Terrain Control Points)',
  1739. group: {
  1740. name: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
  1741. style: GROUP_STYLE_NAME,
  1742. },
  1743. })
  1744. get gizmoTag() {
  1745. return this._gizmotag;
  1746. }
  1747. set gizmoTag(value) {
  1748. this._gizmotag = value;
  1749. // this._refreshAll();
  1750. }
  1751. @property({})
  1752. private _gizmoColor: Color = new Color('#9EECFF');
  1753. @property({
  1754. type: Color,
  1755. tooltip: IN_EDITOR_ZH
  1756. ? '编辑控制点时的gizmo颜色,alpha值最小值为50'
  1757. : ' (Gizmo color when editing control points, alpha value is >=50)',
  1758. displayName: IN_EDITOR_ZH ? '控制点颜色' : ' (Color of Control Points)',
  1759. group: IN_EDITOR_ZH ? '控制点属性' : ' (Points Editing Parameter Control)',
  1760. })
  1761. get gizmoColor() {
  1762. return this._gizmoColor;
  1763. }
  1764. set gizmoColor(value) {
  1765. let a = value.a < 50 ? 50 : value.a;
  1766. a = value.a > 200 ? 200 : value.a;
  1767. // console.log('gizmoColor', this._gizmoColor);
  1768. this._gizmoColor.set(value.r, value.g, value.b, a);
  1769. // this._refreshAll();
  1770. }
  1771. // @property({ type: SpriteFrame })
  1772. // private _gizmotagline: SpriteFrame | null = null;
  1773. // @property({
  1774. // type: SpriteFrame,
  1775. // tooltip: IN_EDITOR_ZH ? '控制点连接纹理' : ' (Texture of Control Lines)',
  1776. // displayName: IN_EDITOR_ZH ? '控制点连接纹理' : ' (Texture of Control Lines)',
  1777. // group: {
  1778. // name: IN_EDITOR_ZH ? '控制点属性' : ' (Terrain Editing Parameter Control)',
  1779. // style: GROUP_STYLE_NAME,
  1780. // },
  1781. // })
  1782. // get gizmoTagLine() {
  1783. // return this._gizmotagline;
  1784. // }
  1785. // set gizmoTagLine(value) {
  1786. // this._gizmotagline = value;
  1787. // // this._refreshAll();
  1788. // }
  1789. private _editNodes: Node[] = [];
  1790. private _lastPositionHash: string = '';
  1791. private _lastSyncPositionHash: string = '';
  1792. private _lastEditNodes: Node[] = null;
  1793. private _padStart(str: string, targetLength: number, padString: string = '0'): string {
  1794. while (str.length < targetLength) {
  1795. str = padString + str;
  1796. }
  1797. return str;
  1798. }
  1799. private _simpleHash(s: string, len: number = 8): string {
  1800. let hash = 5381;
  1801. for (let i = 0; i < s.length; i++) {
  1802. hash = (hash << 5) + hash + s.charCodeAt(i);
  1803. hash = hash & 0xffffffff;
  1804. }
  1805. let hex = (hash >>> 0).toString(16);
  1806. return this._padStart(hex, len).slice(-len);
  1807. }
  1808. private _normalizeAngle(angle: number): number {
  1809. return ((angle % 360) + 360) % 360; // 将角度转化为0-360之间的值 'Convert angle to a value between 0-360'
  1810. }
  1811. private _angleOfVec(vin: Vec2) {
  1812. if (!vin) return 0;
  1813. if (vin.x == 0 && vin.y == 0) return 0;
  1814. let ve2 = v2(vin.x, vin.y);
  1815. let a1 = misc.radiansToDegrees(ve2.signAngle(v2(1, 0)));
  1816. return this._normalizeAngle(-a1);
  1817. }
  1818. //将AssetManager.instance.loadBundle('xxx');封装为一个同步函数,使用的时候可以await调用 'Encapsulate AssetManager.instance.loadBundle('xxx') as a synchronous function, can be called with await'
  1819. // private async loadBundleSync(bundleName: string): Promise<AssetManager.Bundle> {
  1820. // return new Promise((resolve, reject) => {
  1821. // AssetManager.instance.loadBundle(bundleName, (err, bundle) => {
  1822. // if (err) {
  1823. // console.warn('loadBundleSync error: ', err);
  1824. // reject(err);
  1825. // } else {
  1826. // resolve(bundle);
  1827. // }
  1828. // });
  1829. // });
  1830. // }
  1831. // private async loadResSync(bundle: AssetManager.Bundle, path: string): Promise<Asset> {
  1832. // return new Promise((resolve, reject) => {
  1833. // bundle.load(path, (err, res) => {
  1834. // if (err) {
  1835. // console.warn('loadResSync error: ', err);
  1836. // reject(err);
  1837. // } else {
  1838. // resolve(res);
  1839. // }
  1840. // });
  1841. // });
  1842. // }
  1843. private _checkAndAddGizmoNodesByControlPoints() {
  1844. const cpcount = this._controlPoints.length;
  1845. const nodecount = this._editNodes.length;
  1846. console.log(`cpcount`, cpcount);
  1847. console.log(`nodecount`, nodecount);
  1848. let mstart = 0;
  1849. let mend = cpcount;
  1850. for (let m = mstart; m < mend; m++) {
  1851. // console.log('新添加了一个控制点');
  1852. let pos = this._controlPoints[m];
  1853. const n = new Node();
  1854. n.name = 'gizmo_' + (m + 1);
  1855. n.setPosition(pos.x, pos.y + 1, pos.z);
  1856. const opa = n.addComponent(UIOpacity);
  1857. let uit = n.addComponent(UITransform);
  1858. const spc = n.addComponent(Sprite);
  1859. this.node.addChild(n);
  1860. spc.spriteFrame = this._gizmotag;
  1861. uit.setContentSize(80, 80);
  1862. const item = n;
  1863. let sp = item.getComponent(Sprite);
  1864. sp.color = this.gizmoColor;
  1865. if (this._editWhenRun) {
  1866. n.addComponent(MoveWithTouch);
  1867. }
  1868. }
  1869. }
  1870. protected async update(dt: number): Promise<void> {
  1871. //跟随模式下统一处理数据 'Unified data processing in follow mode'
  1872. if (this._syncOnOff && this._syncTarget) {
  1873. const ct = this._syncTarget.getComponent(CurveTexture);
  1874. this._controlPoints = ct!.controlPoints;
  1875. this._vertexesSegment = ct!.vertexesSegment;
  1876. this._updateHeadTail();
  1877. this.node.setPosition(this._syncTarget.position.x, this._syncTarget.position.y);
  1878. if (this.node.getScale().x !== this._syncTarget.scale.x || this.node.getScale().y !== this._syncTarget.scale.y) {
  1879. this.node.setScale(this._syncTarget.scale.x, this._syncTarget.scale.y, 1);
  1880. }
  1881. // this.node.z = this._syncTarget.z;
  1882. const parts: string[] = [];
  1883. this._controlPoints.forEach((item) => parts.push(`${item.x},${item.y};`));
  1884. this._vertexesSegment.forEach((item) => parts.push(`${item.x},${item.y};`));
  1885. let str = parts.join('');
  1886. str += `${this._sortInXDirection}`;
  1887. let hash = this._simpleHash(str);
  1888. //有数据变动才刷新 'Refresh only when there is data change'
  1889. if (hash != this._lastSyncPositionHash) {
  1890. this._lastSyncPositionHash = hash;
  1891. this._refreshAll('1683');
  1892. }
  1893. //清除控制点 'Clear control points'
  1894. this._editNodes = this.node.children.filter((item) => item.name.startsWith('gizmo_'));
  1895. this._editNodes.forEach((item) => {
  1896. item.destroy();
  1897. });
  1898. return;
  1899. }
  1900. if (this._gizmotag == null) {
  1901. // console.warn('需要提前指定gizmo的资源! 拖入 gizmoTag 资源即可' +
  1902. // ' (Gizmo resources need to be specified in advance! Drag in gizmoTag resources)');
  1903. return;
  1904. }
  1905. // if (this._gizmotagline == null) {
  1906. // return;
  1907. // }
  1908. //在编辑器中Ctrl+D,新增控制节点的时候
  1909. if (EDITOR_NOT_IN_PREVIEW) {
  1910. const t = this.node.children.filter((item) => item.name.startsWith('gizmo_'));
  1911. if (this._lastEditNodes != null) {
  1912. const newNodes = t
  1913. .filter((node) => this._lastEditNodes.indexOf(node) === -1)
  1914. .filter((node) => node.name.startsWith('gizmo_'));
  1915. if (newNodes.length > 0) {
  1916. //@ts-ignore
  1917. // let curNodeUUID = await Editor.Selection.getLastSelected('node');
  1918. // console.log(`curNodeUUID `, curNodeUUID);
  1919. //@ts-ignore
  1920. const curNodeUUIDs = await Editor.Selection.getSelected('node');
  1921. // console.log(`curNodeUUIDs `, curNodeUUIDs);
  1922. const curNodeUUID = curNodeUUIDs[curNodeUUIDs.length - 1];
  1923. // 找到curNodeUUIDs中在_lastEditNodes出现的节点,取index最大的那个
  1924. let maxIndex = -1;
  1925. let curNode = null;
  1926. let curNodeIndex = -1;
  1927. for (const uuid of curNodeUUIDs) {
  1928. const idx = this._lastEditNodes.findIndex((item) => item.uuid === uuid);
  1929. if (idx > maxIndex) {
  1930. maxIndex = idx;
  1931. curNode = this._lastEditNodes[idx];
  1932. curNodeIndex = idx;
  1933. }
  1934. }
  1935. let preNode = this._lastEditNodes[curNodeIndex - 1];
  1936. let nextNode = this._lastEditNodes[curNodeIndex + 1];
  1937. let vdirNormal = v2(1, 0);
  1938. if (nextNode) {
  1939. // console.log(`nextNode 节点: ${nextNode.name}`);
  1940. vdirNormal = nextNode.getPosition().clone().subtract(curNode.getPosition()).toVec2().normalize();
  1941. } else if (preNode) {
  1942. // console.log(`preNode 节点: ${preNode.name}`);
  1943. vdirNormal = curNode.getPosition().clone().subtract(preNode.getPosition()).toVec2().normalize();
  1944. }
  1945. if (curNode) {
  1946. let len = 150;
  1947. if (nextNode) {
  1948. len = nextNode.getPosition().clone().subtract(curNode.getPosition()).length();
  1949. len = len / (newNodes.length + 1);
  1950. }
  1951. let sindex = curNode.getSiblingIndex();
  1952. newNodes.forEach((item, index) => {
  1953. // console.log(`设置 newNode : ${item.name}`);
  1954. item.setSiblingIndex(sindex + 1); // siblingIndex会在每次设置后重新计算
  1955. sindex = item.getSiblingIndex();
  1956. const extenLen = len * (index + 1);
  1957. item.setPosition(
  1958. curNode
  1959. .getPosition()
  1960. .toVec2()
  1961. .add(vdirNormal.clone().multiplyScalar(extenLen))
  1962. .add(v2(0, 4 * (index + 1)))
  1963. .toVec3()
  1964. );
  1965. });
  1966. }
  1967. }
  1968. }
  1969. this._lastEditNodes = t;
  1970. }
  1971. this._editNodes.length = 0;
  1972. this._editNodes = this.node.children.filter((item) => item.name.startsWith('gizmo_'));
  1973. if (!this._editWhenRun && !EDITOR_NOT_IN_PREVIEW) {
  1974. this._editNodes.forEach((element) => {
  1975. element.destroy();
  1976. });
  1977. return;
  1978. }
  1979. //初始自动添加编辑控制节点 'Initially automatically add editing control nodes'
  1980. if (this._editNodes.length <= 0) {
  1981. this._checkAndAddGizmoNodesByControlPoints();
  1982. } else if (this._editNodes.length <= 1) {
  1983. // this._checkAndAddGizmoNodesByControlPoints(false);
  1984. this.scheduleOnce(() => {
  1985. this._editNodes.length = 0;
  1986. this._controlPoints.length = 0;
  1987. this._controlPoints.push(v3(200, 200, 0));
  1988. this._controlPoints.push(v3(400, 300, 0));
  1989. }, 0);
  1990. }
  1991. //实时控制节点显示效果 'Real-time control node display effect'
  1992. for (let i = 0; i < this._editNodes.length; i++) {
  1993. const item = this._editNodes[i];
  1994. let uit = item.getComponent(UITransform);
  1995. uit.setContentSize(80, 80);
  1996. let opa = item.getComponent(UIOpacity);
  1997. opa.opacity = 255;
  1998. let sp = item.getComponent(Sprite);
  1999. sp.color = this.gizmoColor;
  2000. sp.spriteFrame = this._gizmotag;
  2001. if (this._editWhenRun) {
  2002. if (item.getComponent(MoveWithTouch) == null) {
  2003. item.addComponent(MoveWithTouch);
  2004. }
  2005. } else {
  2006. const mtouch = item.getComponent(MoveWithTouch);
  2007. if (mtouch) {
  2008. mtouch.destroy();
  2009. }
  2010. }
  2011. }
  2012. if (this._sortInXDirection) {
  2013. //将所有子节点按照X增序排列 'Sort all child nodes in ascending order of X'
  2014. this._editNodes.sort((a, b) => {
  2015. const posA = a.getPosition();
  2016. const posB = b.getPosition();
  2017. return posA.x - posB.x;
  2018. });
  2019. this._editNodes.forEach((item, index) => {
  2020. item.setSiblingIndex(index + 10);
  2021. });
  2022. }
  2023. // for (let i = 1; i < this._editNodes.length; i++) {
  2024. // const pre = this._editNodes[i - 1];
  2025. // const cur = this._editNodes[i];
  2026. // const prePos = pre.getPosition();
  2027. // const curPos = cur.getPosition();
  2028. // if (prePos.x == curPos.x) {
  2029. // cur.setPosition(curPos.x + 120, curPos.y, curPos.z);
  2030. // }
  2031. // }
  2032. for (let j = 0; j < this._editNodes.length; j++) {
  2033. const cur = this._editNodes[j];
  2034. if (j == this._editNodes.length - 1) {
  2035. if (this._headTailCloseOnoff) {
  2036. } else {
  2037. cur.angle = 0;
  2038. break;
  2039. }
  2040. }
  2041. let next = this._editNodes[j + 1];
  2042. if (this._headTailCloseOnoff && j === this._editNodes.length - 1) {
  2043. next = this._editNodes[0];
  2044. }
  2045. const curPos = cur.getPosition();
  2046. const nextPos = next.getPosition();
  2047. const ang = this._angleOfVec(nextPos.clone().subtract(curPos).toVec2());
  2048. cur.angle = ang;
  2049. }
  2050. if (this._sortInXDirection) {
  2051. //再次按照x增序排列 'Sort again in ascending order of x'
  2052. this._editNodes.sort((a, b) => {
  2053. const posA = a.getPosition();
  2054. const posB = b.getPosition();
  2055. return posA.x - posB.x;
  2056. });
  2057. this._editNodes.forEach((item, index) => {
  2058. item.setSiblingIndex(index + 10);
  2059. });
  2060. }
  2061. let str = '';
  2062. this._editNodes.forEach((item) => {
  2063. const pos = item.getPosition();
  2064. str += `${pos.x},${pos.y},${pos.z};`;
  2065. });
  2066. let hash = this._simpleHash(str);
  2067. if (hash !== this._lastPositionHash) {
  2068. this._lastPositionHash = hash;
  2069. this._controlPoints.length = 0;
  2070. this._editNodes.forEach((item) => {
  2071. const pos = item.getPosition();
  2072. this._controlPoints.push(pos);
  2073. });
  2074. this._refreshAll('1567');
  2075. }
  2076. }
  2077. }